package com.atguigu.gulimall.product.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.gulimall.common.constant.ProductConstant;
import com.atguigu.gulimall.common.to.es.SkuEsModel;
import com.atguigu.gulimall.common.utils.R;
import com.atguigu.gulimall.product.entity.*;
import com.atguigu.gulimall.product.feign.SearchFeignClient;
import com.atguigu.gulimall.product.feign.WareFeignService;
import com.atguigu.gulimall.product.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gulimall.common.utils.PageUtils;
import com.atguigu.gulimall.common.utils.Query;

import com.atguigu.gulimall.product.dao.SpuInfoDao;
import org.springframework.transaction.annotation.Transactional;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfoEntity> implements SpuInfoService {

    @Autowired
    private final SkuInfoService skuInfoService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private ProductAttrValueService productAttrValueService;

    @Autowired
    private AttrService attrService;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private SearchFeignClient searchFeignClient;

    public SpuInfoServiceImpl(SkuInfoServiceImpl skuInfoService) {
        this.skuInfoService = skuInfoService;
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SpuInfoEntity> page = this.page(
                new Query<SpuInfoEntity>().getPage(params),
                new QueryWrapper<SpuInfoEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    @Transactional
    public void up(Long spuId) {
        //组装需要的数据
        //1、查出当前spuid对应的所有sku信息、品牌的名字
        List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);
        List<Long> skuIds = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
        //TODO 当前sku所有可以用来被检索的规格属性
        List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrsListForSpu(spuId);
        List<Long> attrIds = baseAttrs.stream().map(ProductAttrValueEntity::getAttrId).collect(Collectors.toList());
        List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);

        //过滤出可以被检索的属性
        Set<Long> idSet = new HashSet<>(searchAttrIds);
        List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> idSet.contains(item.getAttrId()))
                .map(item -> {
                    SkuEsModel.Attrs attr = new SkuEsModel.Attrs();
                    BeanUtils.copyProperties(item, attr);
                    return attr;
                }).collect(Collectors.toList());

        // 发送远程调用,查询是否有库存
        Map<Long, Boolean> stockMap = new HashMap<>();
        try {
            stockMap = wareFeignService.getSkusHasStock(skuIds).getData(new TypeReference<Map<Long, Boolean>>() {});
        }catch (Exception e){
            log.error("库存服务异常：原因:{}",e);
        }

        //2、封装每个sku信息
        Map<Long, Boolean> finalStockMap = stockMap;
        List<SkuEsModel> upProducts = skus.stream().map(sku->{
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku,esModel);

            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());

            //远程调用失败也判定为有库存
            esModel.setHasStock(finalStockMap == null || finalStockMap.get(sku.getSkuId()));
            //热度评分
            esModel.setHotStore(0L);
            //品牌名字
            BrandEntity brand = brandService.getById(sku.getBrandId());
            esModel.setBrandName(brand.getName());
            esModel.setBrandImg(brand.getLogo());

            CategoryEntity category = categoryService.getById(sku.getCatalogId());
            esModel.setCatalogName(category.getName());

            esModel.setAttrs(attrsList);
            return esModel;
        }).collect(Collectors.toList());

        //TODO 发送给检索服务
        R r = searchFeignClient.productStatusUp(upProducts);
        if (r.getCode() == 0){
            //远程调用成功
            //TODO 修改spu状态
            baseMapper.updateSpuStatue(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());
        }else {
            log.error("远程调用失败");
            // 接口幂等性；重试机制
            /**
             * feign 调用流程
             * 1、构造请求数据、将对象转为json
             *  RequestTemplate template = this.buildTemplateFromArgs.create(argv);
             * 2、发送请求进行执行（执行成功会解码相应数据）
             * executeAndDecode(template);
             * 3、执行请求有重试机制
             *  while(true){
             *      try{
             *          executeAndDecode(template);
             *      }catch(){
             *          try{retryer.continueOrPropagate(e);}catch(){throw ex}
             *          continue;
             *      }
             *  }
             */
        }

    }

}